/*
 * Copyright (c) 2022  by Junzhao
 * THIS FILE IS PART OF JAVASE Song Huongkang PROJECT
 * All Rights Reserved.
 */

package Class类;

import org.junit.Test;

import java.lang.annotation.ElementType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

/**
 * @Description Class 类
 * 一个描述类的类
 * 在Object 类中定义了以下的方法，此方法
 * 将被所有子类继承：
 * ●public final Class getClass
 * 以上的方法返回值的类型是一个
 * Class 类，
 * 此类是 Java 反射的源头，实际上所谓反射
 * 从程序的运行结果来看也很好理解，即：
 * 可以通过对象反射求出类的名称。
 * <p>
 * 对象照镜子后可以得到的信息：某个 类 的属性、方法 和构造 器、某个 类到底实现了哪些接
 * 口 。对于 每个类而言， JRE 都 为其保留一个不变 的 Class 类型 的对象。一 个 Class 对象 包含
 * 了 特定 某个结构 class / interface / enum / annotation / primitive type / void / [] 的 有关信息 。见 test005
 * <p>
 * Class 本身也是一 个类
 * Class 对象只能 由系统 建立 对象
 * 一个加载的类 在 JVM 中 只会有一个 Class 实例
 * 一 个 Class 对象对应的是一个加载到 JVM 中的一个 class 文件
 * 每个 类的实例都会记得自己是由 哪个 Class 实例 所 生成
 * 通过 Class 可以完整地得到一个类 中的所有被加载的结构
 * Class 类是 Reflection 的根源，针对任何你想动态加载、运行的类，唯有先获得相应的
 * <></>Class 对象
 * @Author 俊昭
 * @Date 2022/5/6
 */
public class ClassTest {
    /**
     * Class类的常用方法 见ReflectionTest.java -> test002()
     */
    @Test
    public void test004() throws Exception {
        // 获取运行时类的对象
        // static Class forName (String name)
        // 返回指定类名name 的 Class 对象
        Class<Person> clazz = Person.class;

        // Object newInstance() 调用缺省构造函数，返回该Class 对象的一个实例
        Person p1 = clazz.newInstance(); // 实际上在执行过程中还是调用了对应类的空参构造器 且有足够的访问权限

        // getName()  返回此 Class 对象所表示的实体（类、接口、数组类、基本类型 或 void ）名称
        String name = clazz.getName();
        System.out.println(name); // Class类.Person

        // Class getSuperClass() 返回当前 Class 对象的父类的 Class 对象
        Class<? super Person> supP = clazz.getSuperclass();
        System.out.println(supP.getName()); // java.lang.Object

        // Class[] getInterfaces () 获取当前 Class 对象的接口
        Class<ArrayList> c = ArrayList.class;
        Class[] IFs = c.getInterfaces();
        System.out.println(Arrays.toString(IFs));
        // [interface java.util.List,
        //  interface java.util.RandomAccess,
        //  interface java.lang.Cloneable,
        //  interface java.io.Serializable]

        // ClassLoader getClassLoader() 返回该类的类加载器

        // Constructor[] getConstructors 返回一个包含某些 Constructor 对象的数组
        Constructor[] cons = clazz.getConstructors();
        System.out.println(Arrays.toString(cons));
        // [public Class类.Person(java.lang.String,int),
        //  public Class类.Person()]

        // Field[] getDeclaredFields 返回 Field 对象的一个数组
        // Method getMethod (String name,Class … paramTypes )返回一个 Method 对象，此对象的形参类型为 paramType
    }

    /**
     * 获取Class实例的方法
     */
    @Test
    public void test003() throws Exception {
        //4种方式
        //1 若已知具体的类，通过类的 .class 属性获取， 该方法最为安全可靠，程序性能最高
        Class<Person> c1 = Person.class;
        System.out.println(c1);// class Class类.Person

        //2 已知某个类的实例，调用该实例的 getClass() 方法获取Class对象
        Person p1 = new Person();
        Class<? extends Person> c2 = p1.getClass();
        System.out.println(c2);// class Class类.Person

        //3 已知一个类的全类名，且该类在类路径下，可通过Class类的静态方法 forName 获取，可能抛出 ClassNotFoundException
        Class<?> c3 = Class.forName("Class类.Person");// 这里参数要用完整类名
        System.out.println(c3);// class Class类.Person

//        c3 =Class.forName("java.lang.String");
//        System.out.println(c3);// class java.lang.String

        System.out.println(c1 == c2);// true
        System.out.println(c3 == c2);// true
        System.out.println(c1 == c3);// true
        // 虽然获取方式不同 但是是同一个对象 也印证了类只会在内存中加载一个！

        //4 其他方法 通过类加载器
        Class<?> c4 = this.getClass().getClassLoader().loadClass("java.lang.Byte");
        System.out.println(c4);// class java.lang.Byte

    }

    /**
     * 反射学习后 对Person类的操作
     */
    @Test
    public void test001() throws Exception {
        // 通过反射造对象
        Class<Person> c = Person.class;
        Constructor<Person> personC1 = c.getConstructor(String.class, int.class);

        Person p1 = personC1.newInstance("BBB", 18);
        System.out.println(p1.toString());// Person{name='BBB', age=18} // 发现调用的是Person类的toString

        // 通过反射调用方法、属性
        Field age = c.getDeclaredField("age");
        age.set(p1, 10);
        System.out.println(p1);// Person{name='BBB', age=10}

        Method show = c.getDeclaredMethod("show");
        show.invoke(p1);// 我是一个人 我的国籍是 中国

        // 通过反射可以调用类内部私有结构
        // 私有构造器
        Constructor<Person> personC2 = c.getDeclaredConstructor(String.class);
        personC2.setAccessible(true);
        Person p2 = personC2.newInstance("ZZZ");

        System.out.println(p2);// Person{name='ZZZ', age=0}
        // 私有属性、方法
        Field name = c.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p2, "qwe");

        System.out.println(p2);// Person{name='qwe', age=0}

        Method showNation = c.getDeclaredMethod("showNation");
        // 这里除第一个参数是方法名之外 其他都为方法内的形参不过这个是空参方法
        showNation.setAccessible(true);
        System.out.println(showNation.invoke(p1));// 我的国籍是 中国

        Method showNation2 = c.getDeclaredMethod("showNation", String.class);
        // 这里除第一个参数是方法名之外 其他都为方法内的形参
        showNation2.setAccessible(true);
        System.out.println(showNation2.invoke(p1, "China"));// 我的国籍是 :China
        // 这里第一个参数指定操作的对象 第二个参数是传入方法的形参

    }

    /**
     * 学习反射类之前对Person的操作
     */
    @Test
    public void test002() {

        // 造对象
        Person p1 = new Person("GGG", 20);

        // 通过对象调用属性、方法
        p1.age = 21;// 修改属性
        System.out.println(p1);// 调用toString()
        p1.show();

        // 类外部不可以调用类内部的私有结构
        // name showNation() 以及私有构造器

    }

    @Test
    public void test005() {
        // 接口
        Class<Comparator> c1 = Comparator.class;
        // 类
        Class<Object> c2 = Object.class;
        // 字符数组
        Class<String[]> c3 = String[].class;
        // 二维int数组
        Class<int[][]> c4 = int[][].class;
        // 枚举类
        Class<ElementType> c5 = ElementType.class;
        // 注解
        Class<Override> c6 = Override.class;
        // 基本数据类型
        Class<Integer> c7 = int.class;
        // 无返回值的类型
        Class<Void> c8 = void.class;
        // Class类本身
        Class<Class> c9 = Class.class;
        int[] a = new int[10];
        int[] b = new int[100];

        Class<? extends int[]> c10 = a.getClass();
        Class<? extends int[]> c11 = b.getClass();
        // 只要元素类型与维度一样，就是同一个Class
        System.out.println(c10 == c11); //  true 条件 'c10 == c11' 始终为 'true'

        System.out.println(c1.toString() + c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c10 + c11);
        // interface java.util.Comparator
        // class java.lang.Object
        // class [Ljava.lang.String
        // class [[I
        // class java.lang.annotation.ElementType
        // interface java.lang.Override
        // int
        // void
        // class java.lang.Class
        // class [I
        // class [I
        // 可以看到c10 c11 都是 [I
    }

}
